﻿using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using PenguinForce.StateManagement;
using System.Threading;
using Microsoft.Xna.Framework.Input;
using PenguinForce.Entities;
using PenguinForce.Collisions;
using PenguinForce.Levels;

namespace PenguinForce.Screens
{
    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    public class GameplayScreen : GameScreen
    {
        #region Constants

        /// <summary>
        /// Screen width in pixels
        /// </summary>
        private const int screenWidth = 1280;

        /// <summary>
        /// Screen height in pixels
        /// </summary>
        private const int screenHeight = 720;

        /// <summary>
        /// Cursor move speed in pixels per second
        /// </summary>
        private const float cursorMoveSpeed = 250.0f;

        /// <summary>
        /// X location to start drawing the HUD from
        /// </summary>
        private const int hudLocX = 205;
        /// <summary>
        /// Y location to start drawing the HUD from
        /// </summary>
        private const int hudLocY = 30;

        #endregion

        #region Variables

        private ContentManager content;

        // Cursor data
        private Texture2D cursorTexture;
        private Vector2 cursorCenter;
        private Vector2 cursorLocation;
        private ActionMap _cursorController;

        // Where the HUD draws on the screen
        private Vector2 hudLocation;

        private Texture2D bButton;
        private Texture2D xButton;
        private Texture2D yButton;
        private Texture2D onePixelWhite;

        private SnowflakeParticleSystem snowFlakes;
        private BloomComponent m_bloom;

        private HUDRendererComponent _hudRenderer;
        private MessageDisplayComponent _messageDisplay;

        private LevelTransitionTracker _levelTracker;

        

        #endregion

        #region Initialization

        /// <summary>
        /// Constructor.
        /// </summary>
        public GameplayScreen()
        {
            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);

            
        }


        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            _hudRenderer = new HUDRendererComponent(ScreenManager.Game, 300);
            ScreenManager.Game.Components.Add(_hudRenderer);
            ScreenManager.Game.Services.AddService(typeof(IAbilityController), _hudRenderer);

            _messageDisplay = new MessageDisplayComponent(ScreenManager.Game);
            ScreenManager.Game.Components.Add(_messageDisplay);

            if (content == null)
            {
                content = new ContentManager(ScreenManager.Game.Services, "Content");
            }

            bButton = null;
            xButton = null;
            yButton = null;

            // This places the HUD near the upper left corner of the screen
            hudLocation = new Vector2((float)Math.Floor(screenWidth * .1f), (float)Math.Floor(screenHeight * .1f));

            // places the cursor in the center of the screen
            cursorLocation = new Vector2((float)screenWidth / 2, (float)screenHeight / 2);
            _cursorController = new ActionMap(PlayerIndex.One);

            cursorTexture = content.Load<Texture2D>("cursor");
            cursorCenter = new Vector2(cursorTexture.Width / 2, cursorTexture.Height / 2);

            bButton = content.Load<Texture2D>("xboxControllerButtonB");
            xButton = content.Load<Texture2D>("xboxControllerButtonX");
            yButton = content.Load<Texture2D>("xboxControllerButtonY");

            onePixelWhite = new Texture2D(ScreenManager.Game.GraphicsDevice, 1, 1, 0, TextureUsage.None, SurfaceFormat.Color);
            onePixelWhite.SetData<Color>(new Color[] { Color.White });

            m_bloom = new BloomComponent((Game)ScreenManager.Game);
            m_bloom.Settings = new BloomSettings("test", 3.0f, 0.0f, 1.2f, 1.0f, 0.2f, 2.0f);
            
            LoadLevel();

            // A real game would probably have more content than this sample, so
            // it would take longer to load. We simulate that by delaying for a
            // while, giving you a chance to admire the beautiful loading screen.
            //Thread.Sleep(1000);

            // once the load has finished, we use ResetElapsedTime to tell the game's
            // timing mechanism that we have just finished a very long frame, and that
            // it should not try to catch up.
            ScreenManager.Game.ResetElapsedTime();
        }

        private void LoadLevel()
        {
            _levelTracker = new LevelTransitionTracker(ScreenManager.Game);
            cursorLocation = CollisionManager.Instance.GetCurrentLevel().m_startLocation;
            snowFlakes = new SnowflakeParticleSystem((Game)ScreenManager.Game, ScreenManager.SpriteBatch, ScreenManager.Game.Content, 20);
            ScreenManager.Game.Components.Add(snowFlakes);
            ScreenManager.Game.Components.Add(m_bloom);

        }

        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            ScreenManager.Game.Services.RemoveService(typeof(IMessageDisplay));
            ScreenManager.Game.Services.RemoveService(typeof(IAbilityController));
            ScreenManager.Game.Components.Remove(_messageDisplay);
            ScreenManager.Game.Components.Remove(_hudRenderer);
            content.Unload();
        }

        #endregion

        const float TimeBetweenSmokePlumePuffs = 0.0f;
        float timeTillPuff = 0.0f;
        private void UpdateSnowFlakes(float dt)
        {
            timeTillPuff -= dt;
            if (timeTillPuff < 0)
            {
                Vector2 where = Vector2.Zero;
                // add more particles at the bottom of the screen, halfway across.
                where.X = SnowflakeParticleSystem.RandomBetween(-200, ScreenManager.GraphicsDevice.Viewport.Width+200);
                where.Y = -150.0f;

                snowFlakes.AddParticles(where);

                // and then reset the timer.
                timeTillPuff = TimeBetweenSmokePlumePuffs;
            }
        }

        #region Update and Draw


        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
            UpdateSnowFlakes(dt);

            if (!IsActive)
            {
                return;
            }

            if (CollisionManager.Instance.GetCurrentLevel().m_parPenguins <= 0)
            {
                if (_levelTracker.LoadNextLevel())
                {
                    cursorLocation = CollisionManager.Instance.GetCurrentLevel().m_startLocation;
                    ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
                }
                else
                {
                    ScreenManager.AddScreen(new GameOverScreen("You Win!", "You have conquered all of our levels, now go find yourself a real hobby."), ControllingPlayer);
                }
            }

            if (CollisionManager.Instance.Penguins.Count == 0)
            {
                ScreenManager.AddScreen(new GameOverScreen("Game Over", "You're all out of penguins!"), ControllingPlayer);
            }

            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

            HandleInput(elapsedTime);

            CollisionManager.Instance.GetCurrentLevel().Update(gameTime);
            CollisionManager.Instance.Penguins.Update(gameTime, this.ScreenManager.Game.Services.GetService(typeof(IAbilityController)) as IAbilityController);
            foreach (PolarBear bear in CollisionManager.Instance.PolarBears)
            {
                bear.Update(gameTime);
            }
            CollisionManager.Instance.Igloo.TestIntersections();
        }

        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            // Our player and enemy are both actually just text strings.
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;

            Vector2 translationClamp = cursorLocation;

            translationClamp.X = MathHelper.Clamp(translationClamp.X, screenWidth / 2, CollisionManager.Instance.GetCurrentLevel().GetPixelWidth() - screenWidth / 2);
            translationClamp.Y = MathHelper.Clamp(translationClamp.Y, screenHeight / 2, CollisionManager.Instance.GetCurrentLevel().GetPixelHeight() - screenHeight / 2);

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None,
                Matrix.CreateTranslation(new Vector3((-1 * translationClamp) + new Vector2(screenWidth / 2, screenHeight / 2), 0.0f)));

            CollisionManager.Instance.GetCurrentLevel().Draw(gameTime, spriteBatch, Vector2.Zero, translationClamp);

            CollisionManager.Instance.Igloo.Draw(spriteBatch);

            CollisionManager.Instance._stepController.Draw(gameTime, spriteBatch);

            CollisionManager.Instance.Penguins.Draw(spriteBatch, gameTime);
            foreach (PolarBear bear in CollisionManager.Instance.PolarBears)
            {
                bear.Draw(spriteBatch, gameTime);
            }

            // Draw the cursor
            spriteBatch.Draw(cursorTexture, cursorLocation, null, Color.White, 0f, cursorCenter, 1f, SpriteEffects.None, 0f);
            spriteBatch.End();
            
            // If the game is transitioning on or off, fade it out to black.
            if (TransitionPosition > 0)
            {
                ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
            }
        }

        /// <summary>
        /// Read keyboard and gamepad input
        /// </summary>
        private void HandleInput(float elapsedTime)
        {
            _cursorController.BeginUpdate();

            // Allows the game to exit
            if (_cursorController.IsPauseHit())
            {
                ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
            }

            // Update the cursor location by listening for left thumbstick input on
            // the GamePad and direction key input on the Keyboard, making sure to
            // keep the cursor inside the screen boundary
            cursorLocation.X += _cursorController.CurrentGamePadState.ThumbSticks.Left.X * cursorMoveSpeed * elapsedTime;
            cursorLocation.Y -= _cursorController.CurrentGamePadState.ThumbSticks.Left.Y * cursorMoveSpeed * elapsedTime;

            cursorLocation.X = MathHelper.Clamp(cursorLocation.X, 0f, CollisionManager.Instance.GetCurrentLevel().GetPixelWidth());
            cursorLocation.Y = MathHelper.Clamp(cursorLocation.Y, 0f, CollisionManager.Instance.GetCurrentLevel().GetPixelHeight());

            // Add the cursor's location to the WaypointList if the user pressed A on  the GamePad or on the Keyboard.
            if (_cursorController.IsButtonHit(Buttons.A))
            {
                if (!_hudRenderer.IsFishTriggered)
                {
                    CollisionManager.Instance.Penguins.AddWaypoint(cursorLocation);
                    _hudRenderer.TriggerFish();
                }
            }

            if (_cursorController.IsButtonHit(Buttons.Y))
            {
                if (!_hudRenderer.IsSlipperyTriggered)
                {
                    FadingLayer newLayer = new FadingLayer(cursorLocation, 3, 3.0f, CollisionManager.Instance.GetCurrentLevel(), Tile.TileTypes.SlipperyIce);
                    CollisionManager.Instance.GetCurrentLevel().AddFadingLayer(newLayer);
                    _hudRenderer.TriggerSlippery();
                }

            }

            if (_cursorController.IsButtonHit(Buttons.B))
            {
                if (!_hudRenderer.IsSandTriggered)
                {
                    FadingLayer newLayer = new FadingLayer(cursorLocation, 3, 3.0f, CollisionManager.Instance.GetCurrentLevel(), Tile.TileTypes.Sand);
                    CollisionManager.Instance.GetCurrentLevel().AddFadingLayer(newLayer);
                    _hudRenderer.TriggerSand();
                }
            }

            if (_cursorController.CurrentGamePadState.Triggers.Left != 0)
            {
                // Trigger Siren
                _hudRenderer.TriggerSiren();

            }
            if (_cursorController.CurrentGamePadState.Triggers.Right != 0)
            {
                // Trigger Siren
                _hudRenderer.TriggerSiren();

            }

            // Delete all the current waypoints and reset the tanks’ location if 
            // the user pressed X on the GamePad or on the Keyboard.
            if (_cursorController.IsButtonHit(Buttons.X))
            {
                CollisionManager.Instance.Penguins.ClearWaypoints();
            }

            _cursorController.EndUpdate();
        }

        #endregion
    }
}